/*
 *  Copyright (C) 2009  Peter Kist & Jan Ripke
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "Manager.h"
#include "Consumer.h"
#include "Producer.h"
#include "KeyMapper.h"
#include "KeyEventHandler.h"
#include "MouseEventHandler.h"

#include <string>

#include <Util/Logger.h>
#include <Util/Timer.h>

#include <Core/Kernel/AppContext.h>
#include <Core/Kernel/Galaxy.h>
#include <Core/Mediator/Event.h>
#include <Core/Mediator/InterMediator.h>
#include <Core/Interface/Gfx.h>

#include <OISInputManager.h>
#include <OISMouse.h>
#include <OISKeyboard.h>
#include <OISJoyStick.h>

using namespace boost::posix_time;

namespace galaxy {
    namespace input {
        namespace ois_input {

            Manager::Manager(void)
            {
                m_InputManager = 0L;
                m_Mouse = 0L;

                m_Keyboard = 0L;
                m_KeyEventHandler = 0L;
            }

            Manager::~Manager(void)
            {
                terminate ();
            }

            bool Manager::initialize () {
                m_Producer.reset(new Producer(*this));
                m_Consumer.reset(new Consumer(*this));
                m_KeyMap.reset(new KeyMapper());

                mediator::InterMediator::registerProducer (*m_Producer);

                return true;
            }

            bool Manager::postInitialize () {
                mediator::RequestResultPtr RequestResult;
                mediator::Request Request (mediator::CATEGORY_GFX, Request_TYPE_GFX_WINDOW_HANDLE, 0);

                mediator::InterMediator::RequestProducer (Request, RequestResult);

                gfx::GfxRequestWinInfo * winInfo = (gfx::GfxRequestWinInfo *) RequestResult.get();
                if (winInfo) {
                    setupInputSystem (*winInfo);
                } else {
                    LOG_ERROR ("No window info available");
                    return false;
                }

                initializeKeyMapper();

                return true;
            }

            /** start the logic handler thread. The thread is registered by the caller and once this (and the other
            * registered threads) terminate then the game will automatically exit
            */
            boost::thread * Manager::startManagerThread () {
                boost::shared_ptr<Manager> myself = shared_from_this();
                m_InputHandlerThread = new InputHandlerThread (myself);

                boost::thread * thrd = new boost::thread(*m_InputHandlerThread);
                // give thread a chance to start
                while (m_InputHandlerThread->m_State != util::Thread::THREAD_STATE_RUNNING) {
                    boost::this_thread::yield();
                }
                return thrd;
            }

            void Manager::terminateThread () {
                m_InputHandlerThread->m_State = util::Thread::THREAD_STATE_STOP_REQUESTED;
            }

            bool Manager::preDestroy () {
                return true;
            }

            bool Manager::destroy () {
                return true;
            }



            bool Manager::setupInputSystem (gfx::GfxRequestWinInfo const & winInfo) {

                boost::shared_ptr<Manager> myself = shared_from_this();

                OIS::ParamList pl;
                pl.insert(std::make_pair(std::string("WINDOW"), winInfo.windowHandle));
                m_InputManager = OIS::InputManager::createInputSystem(pl);

                m_KeyEventHandler = new KeyEventHandler(myself);
                m_Keyboard = (OIS::Keyboard *) m_InputManager->createInputObject(OIS::OISKeyboard, true);
                m_Keyboard->setEventCallback (m_KeyEventHandler);

                m_MouseEventHandler = new MouseEventHandler(myself);
                m_Mouse = (OIS::Mouse *) m_InputManager->createInputObject(OIS::OISMouse, true);
                m_Mouse->setEventCallback (m_MouseEventHandler);

                OIS::MouseState const & ms = m_Mouse->getMouseState();
                ms.width = winInfo.width;
                ms.height = winInfo.height;

                return true;
            }

            void Manager::initializeKeyMapper () {
                m_KeyMap->loadKeyMap (std::string("MainMenu"));
            }

            void Manager::capture( void )  {
                m_Keyboard->capture();
                m_Mouse->capture();
            }

            bool Manager::terminate () {

                if (m_Keyboard) {
                    m_InputManager->destroyInputObject(m_Keyboard);
                    m_Keyboard = (OIS::Keyboard *) 0L;
                }
                if (m_Mouse) {
                    m_InputManager->destroyInputObject(m_Mouse);
                    m_Mouse = (OIS::Mouse *) 0L;
                }
                if (m_InputManager) {
                    OIS::InputManager::destroyInputSystem (m_InputManager);
                    m_InputManager = (OIS::InputManager *) 0L;
                }

                return true;
            }

            /********************
            * InputHandlerThread
            *********************/

            void InputHandlerThread::operator()() {
                util::Timer timer;

                m_Manager->m_InputHandlerThread->m_State = util::Thread::THREAD_STATE_RUNNING;
                while (m_Manager->m_InputHandlerThread->m_State == util::Thread::THREAD_STATE_RUNNING) {
                    timer.updateTimer ();

					// make ctrl/alt and shift behave correctly
					// TODO: check if messagepump is necessairy
                    //Ogre::WindowEventUtilities::messagePump ();
                    m_Manager->capture();

                    // limit capturing input to 10 times per second.
                    time_duration elapsed = timer.getElapsedDuration ();
                    if (elapsed < milliseconds(10)) {
                        boost::this_thread::sleep(milliseconds(10) - elapsed);
                    }

                }
            }

        } // namespace ois_input

    } // namespace input

} // namespace galaxy
